﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BaseUnits;

namespace Formulas
{
    public class DispatchOM : IOperatingMargin 
    {
        IPowerPlant powerPlant;
        DatabaseInterface.GeneralInfo generalInfo;
        IPowerGrid grid;
        
        public IPowerPlant PowerPlant
        {
            get { return powerPlant; }
            set { powerPlant = value; }
        }
        public DispatchOM(IPowerPlant powerPlant, DatabaseInterface.GeneralInfo generalInfo)
        {
            this.powerPlant = powerPlant;
            this.generalInfo = generalInfo;
            BaseUnits.Builders.GridBuilder gbuilder = new BaseUnits.Builders.GridBuilder(new DatabaseInterface.Connection());
            this.grid = gbuilder.GetGrid(powerPlant.PowerPlantId);
        }
        public double GetOM(DateTime dateFrom, DateTime dateTo)
        {
            return GetOMPeriod(dateFrom, dateTo);
        }
        public double GetOMPeriod(DateTime fromDate, DateTime toDate)
        {
            int yearFrom = fromDate.Year;
            int yearTo = toDate.Year;
            //EGpj,y
            //double totalProjectDispatch = powerPlant.TotalElectricityDispatch(fromDate, toDate);
            double totalProjectDispatch = powerPlant.TotalOutgoing(fromDate, toDate);
            //EGpj,h
            double hourProjectDispatch = 0;
            //EFel,dd,h
            double emmissionFactorTopUnits = 0;
            double totalSum = 0;
            double OM = 0;
            while (fromDate <= toDate)
            {
                hourProjectDispatch = powerPlant.GetOutgoing(fromDate);
                emmissionFactorTopUnits = GetEmissionFactor(fromDate);
                totalSum = totalSum + (hourProjectDispatch * emmissionFactorTopUnits);
                fromDate = fromDate.AddHours(1);
            }
            OM = totalSum / totalProjectDispatch;
            return OM;
        }
        /// <summary>
        /// Returns the total amount of hours in year
        /// </summary>
        /// <param name="year"></param>
        /// <returns></returns>
        private int TotalHoursInYear(int year)
        {
            int totalHours = 8760;
            if (DateTime.IsLeapYear(year))
            {
                totalHours += 24;
            }
            return totalHours;
        }
        
        public List<IPowerUnit> GetTopPowerUnits(DateTime date)
        {
            //double totalDispatch = this.generalInfo.GetTotalDispatch(date);
            double totalDispatch = this.grid.GetOutgoing(date);
            double projectDispatch = this.powerPlant.GetElectricityDispatch(date);
            if (totalDispatch <= 0)
            {
                throw new Exception("No dispatch data available for date: " + date.ToShortDateString() + " hour: "+date.Hour);
            }
            
            double percentage = Math.Max(0.10, projectDispatch / totalDispatch);

            double totalAdded = 0;
            List<IPowerUnit> topPowerUnits = new List<IPowerUnit>();
            List<IPowerUnit> allUnits = this.grid.SortedPowerUnits(date);
            double dispatchPercentage = percentage * totalDispatch;
            bool belongs;
            int ultimo = allUnits.Count - 1;
            while ((totalAdded < dispatchPercentage)&&(allUnits.Count != 0))
            {
                belongs = IsPartOfProject(allUnits[ultimo]);
                if ((!belongs) && (!allUnits[ultimo].IsCDM))
                {
                    topPowerUnits.Add(allUnits[ultimo]);
                    totalAdded += allUnits[ultimo].GetOutgoing(date);
                }
                allUnits.RemoveAt(ultimo);
                ultimo = allUnits.Count - 1;

            }
            return topPowerUnits;
        }
        private bool IsPartOfProject(IPowerUnit powerUnit)
        {
            bool belongs = false;
            foreach (IPowerUnit p in this.powerPlant.PowerUnits)
            {
                if (p.ID == powerUnit.ID)
                {
                    belongs = true;
                }
            }
            return belongs;
        }

        /// <summary>
        /// Returns the emission factor of the power units in the top dispatch
        /// </summary>
        /// <param name="year"></param>
        /// <param name="hour"></param>
        /// <returns></returns>
        public double GetEmissionFactor(DateTime date)
        {
            //IPowerGrid grid = this.powerPlant.Grid;
            List<IPowerUnit> topPowerUnits = GetTopPowerUnits(date);
            double totalEmission = 0;
            double totalDispatch = 0;
            double unitDispatch = 0;
            foreach (IPowerUnit pu in topPowerUnits)
            {
                unitDispatch = pu.GetOutgoing(date);
                totalEmission += (pu.EmissionFactor(date) * unitDispatch);
                totalDispatch += unitDispatch;
            }
            if (totalDispatch <= 0)
            {
                throw new Exception("No dispatch data available");
            }
            double emissionFactor = totalEmission / totalDispatch;
                return emissionFactor;
        }
    }
}
